#pragma once

#include <iostream>
#include <cmath>
#include <algorithm>


const double pi = 3.14159265358979323846;
const double pi_2 = 0.7978845608028654;

template <typename Derived, typename T>
struct Activation {
    static T apply(T x) {
        return Derived::apply(x);  // 调用派生类的实现
    }

    static T derivative(T x) {
        return Derived::derivative(x);
    }
};

// ReLU 实现
template <typename T>
struct ReLU : Activation<ReLU<T>, T> {
    static T apply(T x) {
        return x > 0 ? x : 0;
    }

    static T derivative(T x) {
        return x > 0 ? 1 : 0;
    }
};

template <typename T>
struct Sigmod : Activation<Sigmod<T>, T> {
    static T apply(T x) {
        return 1. / (1. + std::exp(-x));
    }

    static T derivative(T x) {
        T s = apply(x);
        return s * (1 - s);
    }
};

template <typename T>
struct Tanh : Activation<Tanh<T>, T> {
    static T apply(T x) {
        return std::tanh(x);
    }

    static T derivative(T x) {
        T s = apply(x);
        return 1 - std::tanh(x) * std::tanh(x);
    }
};

template <typename T>
struct Sin : Activation<Sin<T>, T> {
    static constexpr T PI = T(pi);
    static constexpr T PI_2 = PI / T(2);

    static T apply(T x) {
        x = std::clamp(x, -PI_2 + T(1e-6), PI_2 - T(1e-6));
        return std::sin(x);
    }

    static T derivative(T x) {
        x = std::clamp(x, -PI_2 + T(1e-6), PI_2 - T(1e-6));
        return std::cos(x);
    }
};

template <typename T>
struct SiLu : Activation<SiLu<T>, T> {
    static T apply(T x) {
        return x * Sigmod<T>::apply(x); 
    }

    static T derivative(T x) {
        T sigmoid_x = Sigmod<T>::apply(x);
        return sigmoid_x * (x + 1); 
    }
};

template <typename T>
struct GeLu : Activation<GeLu<T>, T> {
    static constexpr T SQRT_2_OVER_PI = T(pi_2); // sqrt(2 / pi)
    static constexpr T GELU_COEFF = T(0.044715);
    static constexpr T K_TIMES_3C = SQRT_2_OVER_PI * T(3) * GELU_COEFF; // k * 3c

public:
    static T apply(T x) {
        T gx = SQRT_2_OVER_PI * (x + GELU_COEFF * x * x * x);
        T tanh_gx = std::tanh(gx);
        return T(0.5) * x * (T(1) + tanh_gx);
    }

    static T derivative(T x) {
        T gx = SQRT_2_OVER_PI * (x + GELU_COEFF * x * x * x);
        T tanh_gx = std::tanh(gx);
        T one_minus_tanh_sq = T(1) - tanh_gx * tanh_gx;
        T g_prime = SQRT_2_OVER_PI * (T(1) + T(3) * GELU_COEFF * x * x); // k*(1 + 3c x²)

        T term1 = T(0.5) * (T(1) + tanh_gx);                    // 0.5*(1 + tanh(gx))
        T term2 = T(0.5) * x * one_minus_tanh_sq * g_prime;    // 0.5*x*(1-tanh²)*g'

        return term1 + term2;
    }
};

